---
title: Handling Issues
sidebar:
  order: 3
---

import { Tabs, TabItem } from '@astrojs/starlight/components';

A long list of unused items can be frustrating. The list may contain false
positives, but also things that can actually be removed from the codebase. You
can get a lot of value out of Knip, but sometimes it requires some initial
configuration.

This page guides you in dealing with false positives. It makes sense to go over
the issue types one by one. For instance, reducing the number of unused files
will also reduce the number of unused dependencies. It's recommended to work
this list from top to bottom.

If you haven't already, it will probably help if you've made yourself familiar
with the following topics:

- [Configuration][1]
- [Entry files][2]
- [Configuring project files][3]
- [Using monorepos & workspaces?][4]

## Unused files

Files are reported as unused if they are in the set of project files, but not in
the set of files resolved from the entry files:

```
unused files = project files - (entry files + resolved files)
```

In this section we'll look into common patterns that cause unused files and how
to handle them.

:::tip

Use `--include files` to [filter the report][5] by unused files:

```sh
knip --include files
```

:::

This works and might help with any other issue type as well. For instance, use
`--dependencies` to focus only on dependencies and omit issues related to unused
files and exports.

### Implicit imports

Some files are imported by other tooling, such as fixtures, mocks or templates.
And some frameworks import files such as routes or models, and Knip doesn't have
a plugin for that framework yet.

If such files are in the set of project files, but not imported by your code,
they might be reported as unused files. Exclude them from the `project` files
using negated `project` patterns:

```json
{
  "project": ["src/**/*.ts", "!src/**/__mocks__/**"]
}
```

On the other hand, if such files should be included (because [no plugin exists
for those files yet][6]), the `entry` file patterns can be extended:

```json
{
  "entry": ["src/index.ts", "src/models/*.ts"]
}
```

Also see [configuring project files][7] to learn more about `entry` and
`project` files, and when to use `ignore` patterns.

### Plugins

#### Missing plugins

You might be using a tool or framework that's not in the list of available
plugins. Configuration and entry files (and related dependencies) may be
reported as unused because there is no plugin yet that includes those files. For
example, if `tool.config.js` contains a reference to `@tool/package` then both
the file and the dependency may be reported as an unused.

[Create a new plugin][8] for tools or frameworks that are not [in the list][9]
yet, or [request it][10].

#### Existing plugins

Files may be reported as unused if existing plugins do not include that entry
file pattern yet.

See the [plugins section of entry files][11] for more details. [Override plugin
configuration][12] to customize default patterns for existing plugins.

### Non-standard files

Files might be imported through files with non-standard extensions like
`.astro`, `.mdx`, `.vue` or `.svelte`. These files are not included by default.
See [compilers][13] for more details on how to include them.

### Integrated monorepos

Multiple instances of configuration files like `.eslintrc` and
`jest.config.json` across the repository may be reported as unused when working
in a (mono)repo with a single `package.json`. See [integrated monorepos][14] for
more details and how to configure plugins to target those configuration files.

### Build artifacts and ignored files

Sometimes build artifacts and `.gitignore` files may have a surprising effects
on files reported as unused. Results may be different in separate runs,
depending on the presence of build artifacts. Knip tries to do the right thing,
but in some cases you may need to add a file to the `entry` file patterns
manually for better or more consistent results.

## Unused dependencies

Dependencies imported in unused files are reported as unused dependencies.
That's why it's strongly recommended to try and remedy [unused files][15] first.
This solves many cases of reported unused dependencies.

:::tip

Use the `--dependencies` flag to [filter][5] dependency related issues:

```sh
knip --dependencies
```

:::

### Plugins

If a plugin exists and the dependency is referenced in the configuration file,
but its custom dependency finder does not detect it, then that's a false
positive. Please open a pull request or issue to fix it.

Adding the configuration file as an `entry` file pattern may be a temporary
stopgap that fixes your situation, but it's better to create a new plugin or fix
an existing one.

### Non-standard files

Dependencies might be imported from files with non-standard extensions like
`.astro`, `.mdx`, `.vue` or `.svelte`. These files are not included by default.
See [compilers][13] for more details on how to include them.

### Unreachable code

If the reference to a dependency is unrecognizable or unreachable to Knip, and
you don't feel like a plugin could solve it, a last resort is to ignore it:

```json
{
  "ignoreDependencies": ["ignore-me", "@problematic/package"]
}
```

Depending on the situation, you may want to use `ignoreBinaries` instead. See
[unlisted binaries][16].

### ESLint & Jest

Within monorepos, tools like ESLint and Jest are a story of their own. Sharing
and extending configurations is convenient, but for a project linter like Knip
it can be a challenge to assign dependencies to the right workspace. Jest has
comparable characteristics.

ESLint is still in the process of moving to a modern configuration system, which
results in the recommendation going forward: migrate to the new [ESLint flat
config system][17].

Unfortunately there's currently no clean way to assign (unused or unlisted)
dependencies to another workspace.

There is a workaround, though. For example, workspaces that use the
`@internal/eslint-config` package can force-enable the ESLint plugin (without
having `eslint` listed) and ignore related dependencies (adjust to your
situation):

```json
{
  "eslint": true,
  "ignoreDependencies": ["^eslint-.*"]
}
```

## Unlisted dependencies

This means that a dependency is used, but not listed in `package.json`.

An unlisted dependency is usually a transitive dependency that's imported
directly. The dependency is installed (since it's a dependency of another
dependency) and lives in `node_modules`, but it's not listed explicitly in
`package.json`.

You should not rely on transitive dependencies for various reasons, including
control, security and stability. The solution is to install and list the
dependency in `dependencies` or `devDependencies`.

## Unlisted binaries

Binaries are executable Node.js scripts. Many npm packages, when installed, add
an executable file to use from scripts in `package.json`. Examples include
TypeScript with the `tsc` binary, Next.js with the `next` binary, and so on.

Knip detects such binaries in scripts and checks whether there's package
installed that includes the binary. It looks up the `bin` field in the
`package.json` file of installed packages. If it doesn't find it, it will be
reported as an unlisted binary as there is no package listed that contains it.
Except for those listed as `IGNORED_GLOBAL_BINARIES` in `constants.ts`.

### Missing binaries

In case unused (dev) dependencies look like a match against unlisted binaries,
then this might be caused by `node_modules` not containing the packages. And
this might have been caused by either the way your package manager installs
dependencies, or by not running Knip from the root of the repository.

Knip should run from the root. But you can [lint individual workspaces][18].

### Example

Sometimes their usage or the way Knip reports them can be a bit confusing. See
this example:

```json
{
  "name": "lib",
  "scripts": {
    "commitlint": "commitlint --edit"
  },
  "devDependencies": {
    "@commitlint/cli": "*"
  }
}
```

This example works fine without anything reported, as the `@commitlint/cli`
package includes the `commitlint` binary. However, some script may contain
`npx commitlint` and here Knip assumes `commitlint` is the name of the package.
This technically works as `commitlint` is a transitive dependency of
`@commitlint/cli`, but to avoid confusion it's recommended to use
`npx @commitlint/cli`.

## npx

For `npx` scripts, Knip assumes that `--yes` (as in `npx --yes package`) means
that the package is not listed. Knip expects the dependency to be listed with
`--no` or no flag at all.

The recommendation here is to be explicit: use `--yes` if the dependency is not
supposed to be listed in `package.json`. Or use `--no` if the dependency is
listed.

## Unused exports

By default, Knip does not report unused exports of `entry` files.

When unused exports are reported, and you want to keep exporting those, there
are a few options:

- [Ignore exports used in file][19] for exports used internally.
- Individual exports can be [tagged using JSDoc syntax][20].
- Have the export in or re-exported by an [entry file][2]:
  - Add the file to the `entry` file patterns array in the configuration
  - Move the export(s) to an entry file
  - Re-export the unused export(s) from an entry file
  - Add the file to the `exports` field of `package.json`

:::tip

Use the `--exports` flag to [filter][5] and focus only on issues related to
exports:

```sh
knip --exports
```

:::

### External libraries

Are the exports consumed or imported by an external library, resulting in a
non-standard consumption of your exports? Try the `--include-libs` flag.

Here's an example:

<Tabs>
  <TabItem label="index.js">

    ```ts
    import loadable from '@loadable/component';

    export const DynamicApple = dynamic(() =>
      import('./components.js').then(mod => mod.Apple)
    );

    export const LoadableOrange = loadable(() => import('./components.js'), {
      resolveComponent: components => components.Orange,
    });
    ```

  </TabItem>

  <TabItem label="components.js">

    ```ts
    export const Apple = () => 'Apple';
    export const Orange = () => 'Orange';
    ```

  </TabItem>
</Tabs>

Knip understands `Apple` is used, since it's standard usage. But `Orange` is
referenced through a function of an external library. For performance reasons,
Knip does not include external type definitions by default so it won't see the
export being referenced.

To include the type definitions of external libraries, use the
[--include-libs][21] flag:

```shell
knip --include-libs
```

This comes at a performance and memory penalty, but should give better results
if you need it. This flag is implied when [classMembers][22] are included (that
feature comes with roughly the same performance penalty).

### Missing exports?

Do you expect certain exports in the report, but are they missing? They might be
exported from an entry file. Use [--include-entry-exports][23] to make Knip also
report unused exports in entry files.

## Class members

Unused class members are not reported by default, here's how to enable them:

```sh
knip --include classMembers
```

This option is also available in the Knip configuration file. Note that this
feature comes at a cost: linting will take more time and more memory (in rare
cases it may even run out of memory in large repositories).

Individual class members can be [tagged using JSDoc syntax][20].

Classes exported from entry files are ignored, and so are their members. Use
[--include-entry-exports][23] to make Knip also report members of unused exports
in entry files.

## Enum members

Unused enums and unused members of enums are reported by default. Reporting such
members can also be disabled altogether, for example:

```sh
knip --exclude enumMembers
```

Individual enum members can be [tagged using JSDoc syntax][20].

Enums exported from entry files are ignored, and so are their members. Use
[--include-entry-exports][23] to make Knip also report members of unused exports
in entry files.

## False positives

If you believe Knip incorrectly reports something as unused (i.e. a false
positive), this could be caused by one of multiple reasons:

- Knip requires additional configuration, for instance additional [entry
  files][2] or [paths][24] should be added.
- Knip has a bug. You can help your own project and Knip by creating a [minimal
  reproduction][25] and open an issue on GitHub.

Also see [troubleshooting][26] if you haven't already.

[1]: ../overview/configuration.md
[2]: ../explanations/entry-files.md
[3]: ../guides/configuring-project-files.md
[4]: ../features/monorepos-and-workspaces.md
[5]: ../features/rules-and-filters.md#filters
[6]: #missing-plugins
[7]: ./configuring-project-files.md
[8]: ./writing-a-plugin.md
[9]: ../reference/plugins.md
[10]: https://github.com/webpro-nl/knip/issues/483
[11]: ../explanations/plugins.md#entry-files
[12]: ../explanations/entry-files.md#plugins
[13]: ../features/compilers.md
[14]: ../features/integrated-monorepos.md
[15]: #unused-files
[16]: #unlisted-binaries
[17]: https://eslint.org/docs/latest/use/configure/configuration-files-new
[18]: ../features/monorepos-and-workspaces.md#lint-a-single-workspace
[19]: ../reference/configuration.md#ignoreexportsusedinfile
[20]: ../reference/jsdoc-tsdoc-tags.md
[21]: ../reference/cli#--include-libs
[22]: #class-members
[23]: ../reference/configuration.md#includeentryexports
[24]: ../reference/configuration.md#paths
[25]: ../guides/troubleshooting.md#minimal-reproduction
[26]: ./troubleshooting.md
